//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File: RootBus.h
//  Programmer: Visual.Wei (2007)
//
#ifndef __ROOTBUS_H_
#define __ROOTBUS_H_
#include <DefBus.h>
#include "Clock.h"
#define MAX_INITPARAM 0x20

//------------------------------------------------------------------------------

class S3c2443DeviceFolder;

//------------------------------------------------------------------------------

class RootBus : public DefaultBusDriver, public S3c2443Clock
{
public : 
    RootBus(LPCTSTR lpActiveRegPath) ;
    virtual ~RootBus();
    virtual BOOL Open(DWORD dwAccessCode, DWORD dwShare);
    virtual BOOL Init(); 
    virtual BOOL PostInit() ;
    virtual BOOL AssignChildDriver();
    virtual BOOL ActivateAllChildDrivers();
    virtual DWORD GetBusNamePrefix(LPTSTR lpReturnBusName, DWORD dwSizeInUnit);
    virtual BOOL FastIOControl(
        DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, 
        DWORD dwLenOut, PDWORD pdwActualOut, DeviceFolder **ppDeviceFoler
    );
    virtual BOOL PowerUp();
    virtual BOOL PowerDown();
    
protected:
    S3c2443DeviceFolder* CreateNewDevice(CRegistryEdit& subKey, LPCTSTR lpName);

protected:
    LPTSTR  m_lpActiveRegPath;
    CRegistryEdit m_DeviceKey;
    DWORD   m_dwBusType;
    LPTSTR  m_lpBusName;
    DWORD   m_dwBusNumber;
    DWORD   m_dwDeviceIndex;
    
    LPTSTR  m_lpStrInitParam;
    DWORD   m_dwNumOfInitParam;
    LPTSTR  m_lpInitParamArray[MAX_INITPARAM];

};

//------------------------------------------------------------------------------

class S3c2443DeviceFolder : public DeviceFolder {

public:
    S3c2443DeviceFolder(
        LPCTSTR lpBusName, LPCTSTR lpTemplateRegPath, DWORD dwBusType, 
        DWORD BusNumber, DWORD DeviceNumber, DWORD FunctionNumber,
        HANDLE hParent, DDKWINDOWINFO * pdwi, RootBus * pRootBus, 
        DWORD dwMaxInitReg = MAX_INIT_REG, LPCTSTR lpDeviceBusName = NULL
    ) : DeviceFolder(
        lpBusName, lpTemplateRegPath, dwBusType, BusNumber, DeviceNumber,
        FunctionNumber, hParent, dwMaxInitReg, lpDeviceBusName
    ), m_RootBus(pRootBus) {
        // Device Default Power State
        m_CurActualPowerState = D0;
        m_CurRequestPowerState = D4;
        // Device Location.
        memset(&m_DevLoc, 0, sizeof(m_DevLoc));
        if (
            pdwi && pdwi->cbSize == sizeof(DDKWINDOWINFO) && 
            pdwi->dwNumMemWindows != 0
        ) {
            m_DevLoc.IfcType = dwBusType;
            m_DevLoc.BusNumber = BusNumber;
            m_DevLoc.LogicalLoc = pdwi->memWindows[0].dwBase;;
        }
        ASSERT(pRootBus!=NULL);
        //Clock associated with this device.
        for (DWORD dwIndex = 0; dwIndex < CLOCK_ALLOCATION_SIZE; dwIndex++) {
            m_bClockRequested[dwIndex] = 0;
            m_clkClock[dwIndex] = pRootBus->AllocateClock(
                &m_DevLoc, (CLOCK_ALLOCATION)dwIndex
            );
        }
    };

    virtual BOOL LoadDevice() {
        // If driver is not to be loaded, go on to next device
        if (GetLoadFlag() & DEVFLAGS_NOLOAD) return FALSE;
        m_CurActualPowerState = m_CurPowerState = D0 ; // We load driver at D0;
        UpdatePowerState();
        BOOL bReturn = DeviceFolder::LoadDevice();
        if (!bReturn) { // Loading Fails
            m_CurActualPowerState = m_CurPowerState = D4 ; // We load driver at D0;
            UpdatePowerState();
        }
        return bReturn;
    }
    
    virtual BOOL UnloadDevice() {
        BOOL bReturn = DeviceFolder::UnloadDevice();
        if (bReturn) { // Unloading Success.
            m_CurActualPowerState = m_CurPowerState = D4 ; // We load driver at D0;
            UpdatePowerState();
        }
        return bReturn;
    }
    
    virtual ~S3c2443DeviceFolder() {
        m_CurActualPowerState  = D4; // Turn Off Power.
        UpdatePowerState();
        for (DWORD dwIndex = 0; dwIndex < CLOCK_ALLOCATION_SIZE; dwIndex ++) {
            if (m_clkClock[dwIndex] != CLK_CLOCK_INVALID) {
                m_RootBus->FreeClock(m_clkClock [dwIndex]);
            }
        }
    };
    
    // If we have multiple Dependency here, we should do differently.
    // We should get dependency ID which is index to dependency array.
    // and evaluate dependency, get m_CurActualPowerState and call
    virtual DWORD  RequestPowerDependentID() { return 0; }
    virtual BOOL ReleasePowerDependent(DWORD dwID) { return TRUE; };

    virtual BOOL SetPowerState(CEDEVICE_POWER_STATE newPowerState) { 
        Lock();
        m_CurPowerState = newPowerState;
        // Take Higher power state.
        m_CurActualPowerState = min(m_CurRequestPowerState, m_CurPowerState);
        BOOL bReturn = UpdatePowerState();
        Unlock();
        return bReturn;
    }
    
    virtual BOOL DependentPowerStateRequest(
        CEDEVICE_POWER_STATE newPowerState, DWORD /*dwID*/
    )  {
        Lock();
        m_CurRequestPowerState = newPowerState ;
        // Take Higher power state        
        m_CurActualPowerState = min(m_CurRequestPowerState, m_CurPowerState);
        BOOL bReturn = UpdatePowerState();
        Unlock();
        return bReturn;
    };
    
protected:
    BYTE      m_bClockRequested[CLOCK_ALLOCATION_SIZE] ;
    CLK_CLOCK m_clkClock[CLOCK_ALLOCATION_SIZE];
    virtual BOOL UpdatePowerState();
    
    BOOL ClkRequest(CLOCK_ALLOCATION id) {
        if (id < CLOCK_ALLOCATION_SIZE && m_clkClock[id] != CLK_CLOCK_INVALID) {
            if (
                m_bClockRequested[id] == 0 && 
                m_RootBus->RequestClock(m_clkClock[id])
            ) m_bClockRequested[id] = 1;
            return m_bClockRequested[id]!=0;
        }
        return FALSE;
    }
    
    BOOL ClkRelease(CLOCK_ALLOCATION id) {
        if (id < CLOCK_ALLOCATION_SIZE && m_clkClock[id] != CLK_CLOCK_INVALID) {
             if (m_bClockRequested[id] != 0) {
                BOOL bReturn=m_RootBus->ReleaseClock(m_clkClock[id]);
                ASSERT(bReturn==TRUE);
                m_bClockRequested[id] = 0;
             }
             return (m_bClockRequested[id] == 0);
        }
        return FALSE;
    }
    
protected:
    CEDEVICE_POWER_STATE m_CurActualPowerState, m_CurRequestPowerState;
    DEVICE_LOCATION m_DevLoc;
    RootBus * const m_RootBus;
    
};

//------------------------------------------------------------------------------

class S3c2443OneClockFolder : public S3c2443DeviceFolder
{

public:
    S3c2443OneClockFolder(
        LPCTSTR lpBusName, LPCTSTR lpTemplateRegPath, DWORD dwBusType,
        DWORD BusNumber, DWORD DeviceNumber, DWORD FunctionNumber,
        HANDLE hParent, DDKWINDOWINFO *pdwi, RootBus* pRootBus, 
        DWORD dwMaxInitReg = MAX_INIT_REG, LPCTSTR lpDeviceBusName = NULL
    ) : S3c2443DeviceFolder(
        lpBusName, lpTemplateRegPath, dwBusType, BusNumber, DeviceNumber,
        FunctionNumber, hParent, pdwi, pRootBus, dwMaxInitReg, lpDeviceBusName
    ) {
    }
    
protected:
    virtual BOOL UpdatePowerState();
};

//------------------------------------------------------------------------------

class S3c2443TwoClocksFolder : public S3c2443DeviceFolder
{

public:
    S3c2443TwoClocksFolder(
        LPCTSTR lpBusName, LPCTSTR lpTemplateRegPath, DWORD dwBusType,
        DWORD BusNumber, DWORD DeviceNumber, DWORD FunctionNumber,
        HANDLE hParent, DDKWINDOWINFO *pdwi, RootBus* pRootBus, 
        DWORD dwMaxInitReg = MAX_INIT_REG, LPCTSTR lpDeviceBusName = NULL
    ) : S3c2443DeviceFolder(
        lpBusName, lpTemplateRegPath, dwBusType, BusNumber, DeviceNumber,
        FunctionNumber, hParent, pdwi, pRootBus, dwMaxInitReg, lpDeviceBusName
    ) {
    }
    
protected:
    virtual BOOL UpdatePowerState();
};

//------------------------------------------------------------------------------

class S3c2443ThreeClocksFolder : public S3c2443DeviceFolder
{

public:
    S3c2443ThreeClocksFolder(
        LPCTSTR lpBusName, LPCTSTR lpTemplateRegPath, DWORD dwBusType,
        DWORD BusNumber, DWORD DeviceNumber, DWORD FunctionNumber,
        HANDLE hParent, DDKWINDOWINFO *pdwi, RootBus* pRootBus, 
        DWORD dwMaxInitReg = MAX_INIT_REG, LPCTSTR lpDeviceBusName = NULL
    ) : S3c2443DeviceFolder(
        lpBusName, lpTemplateRegPath, dwBusType, BusNumber, DeviceNumber,
        FunctionNumber, hParent, pdwi, pRootBus, dwMaxInitReg, lpDeviceBusName
    ) {
    }
    
protected:
    virtual BOOL UpdatePowerState();
};

//------------------------------------------------------------------------------

#endif
